package project.one.adk;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import project.one.adk.bluetooth.BluetoothChatService;
import project.one.adk.bluetooth.DeviceListActivity;
import android.app.ActionBar;
import android.app.Activity;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.android.future.usb.UsbAccessory;
import com.android.future.usb.UsbManager;

public class ProjectOneActivity extends Activity implements View.OnTouchListener, ControlView.DirectionListener {

	private static final String TAG = ProjectOneActivity.class.getSimpleName();
	private static final boolean D = true;

	private PendingIntent mPermissionIntent;
	private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
	private boolean mPermissionRequestPending;

	private UsbManager mUsbManager;
	private UsbAccessory mAccessory;
	private ParcelFileDescriptor mFileDescriptor;
	private FileInputStream mInputStream;
	private FileOutputStream mOutputStream;

	private static final byte COMMAND_DIRECTION = 0x2;
	private static final byte GO_FORWARD = 0x1;
	private static final byte GO_BACKWARD = 0x2;
	private static final byte GO_LEFT = 0x3;
	private static final byte GO_RIGHT = 0x4;
	private static final byte GO_FORWARD_STOP = 0x5;
	private static final byte GO_BACKWARD_STOP = 0x6;
	private static final byte GO_LEFT_STOP = 0x7;
	private static final byte GO_RIGHT_STOP = 0x8;
	
	private static final String CMD_FORWARD = "GO_FORWARD";
	private static final String CMD_BACKWARD = "GO_BACKWARD";
	private static final String CMD_LEFT = "GO_LEFT";
	private static final String CMD_RIGHT = "GO_RIGHT";
	private static final String CMD_FORWARD_STOP = "STOP_FORWARD";
	private static final String CMD_BACKWARD_STOP = "STOP_BACKWARD";
	private static final String CMD_LEFT_STOP = "STOP_LEFT";
	private static final String CMD_RIGHT_STOP = "STOP_RIGHT";
	private static final String CMD_ALL_STOP = "STOP_ALL";

	private Button forwardBtn, backwardBtn, leftBtn, rightBtn;
	private Drawable btnBackground;
	
	/* BlueTooth */
    private BluetoothChatService mChatService = null;
    private BluetoothAdapter mBluetoothAdapter = null;
    // Name of the connected device
    private String mConnectedDeviceName = null;
    
    // Intent request codes
    private static final int REQUEST_CONNECT_DEVICE_SECURE = 1;
    private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
    private static final int REQUEST_ENABLE_BT = 3;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mUsbManager = UsbManager.getInstance(this);
		mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(
				ACTION_USB_PERMISSION), 0);
		IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
		filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
		registerReceiver(mUsbReceiver, filter);

//		View v = inflater.inflate(R.layout.main, null, false);
		setContentView(R.layout.main);
		
		ControlView controlView = (ControlView) findViewById(R.id.remote_control_view);
		controlView.setListener(this);
		
//		forwardBtn = (Button) findViewById(R.id.forward_btn);
//		backwardBtn = (Button) findViewById(R.id.backward_btn);
//		leftBtn = (Button) findViewById(R.id.left_btn);
//		rightBtn = (Button) findViewById(R.id.right_btn);	
//		
//		forwardBtn.setOnTouchListener(this);
//		backwardBtn.setOnTouchListener(this);
//		leftBtn.setOnTouchListener(this);
//		rightBtn.setOnTouchListener(this);
		
//		btnBackground = forwardBtn.getBackground();
		
        // Get local Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        
        // If the adapter is null, then Bluetooth is not supported
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
	}

    @Override
    public void onStart() {
        super.onStart();
        if(D) Log.e(TAG, "++ ON START ++");

        // If BT is not on, request that it be enabled.
        // setupChat() will then be called during onActivityResult
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        // Otherwise, setup the chat session
        } else {
        	setupBluetoothService();
        }
    }
	/**
	 * Called when the activity is resumed from its paused state and immediately
	 * after onCreate().
	 */
	@Override
	public void onResume() {
		super.onResume();
        if(D) Log.e(TAG, "+ ON RESUME +");

		if (mInputStream != null && mOutputStream != null) {
			return;
		}

		UsbAccessory[] accessories = mUsbManager.getAccessoryList();
		UsbAccessory accessory = (accessories == null ? null : accessories[0]);
		if (accessory != null) {
			if (mUsbManager.hasPermission(accessory)) {
				openAccessory(accessory);
			} else {
				synchronized (mUsbReceiver) {
					if (!mPermissionRequestPending) {
						mUsbManager.requestPermission(accessory,
								mPermissionIntent);
						mPermissionRequestPending = true;
					}
				}
			}
		} else {
			Log.d(TAG, "mAccessory is null");
		}

        // Performing this check in onResume() covers the case in which BT was
        // not enabled during onStart(), so we were paused to enable it...
        // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
        if (mChatService != null) {
            // Only if the state is STATE_NONE, do we know that we haven't started already
            if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
              // Start the Bluetooth chat services
              mChatService.start();
            }
        }
	}

	/** Called when the activity is paused by the system. */
	@Override
	public void onPause() {
		super.onPause();
		closeAccessory();
	}

	/**
	 * Called when the activity is no longer needed prior to being removed from
	 * the activity stack.
	 */
	@Override
	public void onDestroy() {
		super.onDestroy();
		unregisterReceiver(mUsbReceiver);
        // Stop the Bluetooth chat services
        if (mChatService != null) 
        	mChatService.stop();
        if(D) Log.e(TAG, "--- ON DESTROY ---");
	}
	

	private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (ACTION_USB_PERMISSION.equals(action)) {
				synchronized (this) {
					UsbAccessory accessory = UsbManager.getAccessory(intent);
					if (intent.getBooleanExtra(
							UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
						openAccessory(accessory);
					} else {
						Log.d(TAG, "permission denied for accessory "
								+ accessory);
					}
					mPermissionRequestPending = false;
				}
			} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
				UsbAccessory accessory = UsbManager.getAccessory(intent);
				if (accessory != null && accessory.equals(mAccessory)) {
					closeAccessory();
				}
			}
		}
	};

	private void openAccessory(UsbAccessory accessory) {
		mFileDescriptor = mUsbManager.openAccessory(accessory);
		if (mFileDescriptor != null) {
			mAccessory = accessory;
			FileDescriptor fd = mFileDescriptor.getFileDescriptor();
			mInputStream = new FileInputStream(fd);
			mOutputStream = new FileOutputStream(fd);
			Log.d(TAG, "accessory opened");
		} else {
			Log.d(TAG, "accessory open fail");
		}
	}

	private void closeAccessory() {
		try {
			if (mFileDescriptor != null) {
				mFileDescriptor.close();
			}
		} catch (IOException e) {
		} finally {
			mFileDescriptor = null;
			mAccessory = null;
		}
	}

	public void sendLedSwitchCommand(byte direction) {
		byte[] buffer = new byte[2];
		buffer[0] = COMMAND_DIRECTION;
		buffer[1] = direction;

		if (mOutputStream != null) {
			try {
				mOutputStream.write(buffer);
			} catch (IOException e) {
				Log.e(TAG, "write failed", e);
			}
		}
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
//		switch (event.getAction()) {
//		case MotionEvent.ACTION_DOWN :
//			switch (v.getId()) {
//			case R.id.forward_btn :
//				sendCommandToArduino(GO_FORWARD);
//				forwardBtn.setBackgroundResource(R.color.btn_press);
//				sendCmdMsgToPairedDevice(CMD_FORWARD);
//				return true;
//			case R.id.backward_btn :
//				sendCommandToArduino(GO_BACKWARD);
//				backwardBtn.setBackgroundResource(R.color.btn_press);
//				sendCmdMsgToPairedDevice(CMD_BACKWARD);
//				return true;
//			case R.id.left_btn :
//				sendCommandToArduino(GO_LEFT);
//				leftBtn.setBackgroundResource(R.color.btn_press);
//				sendCmdMsgToPairedDevice(CMD_LEFT);
//				return true;
//			case R.id.right_btn :
//				sendCommandToArduino(GO_RIGHT);
//				rightBtn.setBackgroundResource(R.color.btn_press);
//				sendCmdMsgToPairedDevice(CMD_RIGHT);
//				return true;
//			}
//			break;
//		case MotionEvent.ACTION_UP :
//			switch (v.getId()) {
//			case R.id.forward_btn :
//				sendCommandToArduino(GO_FORWARD_STOP);
//				forwardBtn.setBackgroundDrawable(btnBackground);
//				sendCmdMsgToPairedDevice(CMD_FORWARD_STOP);
//				return true;
//			case R.id.backward_btn :
//				sendCommandToArduino(GO_BACKWARD_STOP);
//				backwardBtn.setBackgroundDrawable(btnBackground);
//				sendCmdMsgToPairedDevice(CMD_BACKWARD_STOP);
//				return true;
//			case R.id.left_btn :
//				sendCommandToArduino(GO_LEFT_STOP);
//				leftBtn.setBackgroundDrawable(btnBackground);
//				sendCmdMsgToPairedDevice(CMD_LEFT_STOP);
//				return true;
//			case R.id.right_btn :
//				sendCommandToArduino(GO_RIGHT_STOP);
//				rightBtn.setBackgroundDrawable(btnBackground);
//				sendCmdMsgToPairedDevice(CMD_RIGHT_STOP);
//				return true;
//			}
//			break;
//		}
		return false;
	}
	
	private synchronized void sendCommandToArduino(byte direction) {
		new AsyncTask<Byte, Void, Void>() {

			@Override
			protected Void doInBackground(Byte... params) {
				sendLedSwitchCommand(params[0]);
				return null;
			}	
		}.execute(direction);
		
		// give a term to arduino board to process command.
//		try {
//			Thread.sleep(100);
//		} catch (InterruptedException e) {
//			Log.d(TAG, e.getMessage());
//		}
	}
	
    private final void setStatus(int resId) {
        final ActionBar actionBar = getActionBar();
        actionBar.setSubtitle(resId);
    }

    private final void setStatus(CharSequence subTitle) {
        final ActionBar actionBar = getActionBar();
        actionBar.setSubtitle(subTitle);
    }
    
    // The Handler that gets information back from the BluetoothChatService
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case BluetoothChatService.MESSAGE_STATE_CHANGE:
                if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
                switch (msg.arg1) {
                case BluetoothChatService.STATE_CONNECTED:
                    setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
                    //mConversationArrayAdapter.clear();
                    break;
                case BluetoothChatService.STATE_CONNECTING:
                    setStatus(R.string.title_connecting);
                    break;
                case BluetoothChatService.STATE_LISTEN:
                case BluetoothChatService.STATE_NONE:
                    setStatus(R.string.title_not_connected);
                    break;
                }
                break;
            case BluetoothChatService.MESSAGE_WRITE:
                byte[] writeBuf = (byte[]) msg.obj;
                // construct a string from the buffer
                String writeMessage = new String(writeBuf);
                //mConversationArrayAdapter.add("Me:  " + writeMessage);
                break;
            case BluetoothChatService.MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                sendCommand(readMessage);
                //mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
                break;
            case BluetoothChatService.MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(BluetoothChatService.DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case BluetoothChatService.MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(BluetoothChatService.TOAST),
                               Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };
    
    private void sendCommand(String cmdMsg) {
    	if (cmdMsg.equals(CMD_FORWARD)) {
    		sendCommandToArduino(GO_FORWARD);
    	} else if (cmdMsg.equals(CMD_BACKWARD)) {
    		sendCommandToArduino(GO_BACKWARD);
    	} else if (cmdMsg.equals(CMD_LEFT)) {
    		sendCommandToArduino(GO_LEFT);
    	} else if (cmdMsg.equals(CMD_RIGHT)) {
    		sendCommandToArduino(GO_RIGHT);
    	} else if (cmdMsg.equals(CMD_FORWARD_STOP)) {
    		sendCommandToArduino(GO_FORWARD_STOP);
    	} else if (cmdMsg.equals(CMD_BACKWARD_STOP)) {
    		sendCommandToArduino(GO_BACKWARD_STOP);
    	} else if (cmdMsg.equals(CMD_LEFT_STOP)) {
    		sendCommandToArduino(GO_LEFT_STOP);
    	} else if (cmdMsg.equals(CMD_RIGHT_STOP)) {
    		sendCommandToArduino(GO_RIGHT_STOP);
    	} else {
    		sendCommandToArduino(GO_FORWARD_STOP);
    		sendCommandToArduino(GO_BACKWARD_STOP);
    		sendCommandToArduino(GO_LEFT_STOP);
    		sendCommandToArduino(GO_RIGHT_STOP);
    	}
    }
    
    private void ensureDiscoverable() {
        if(D) Log.d(TAG, "ensure discoverable");
        if (mBluetoothAdapter.getScanMode() !=
            BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivity(discoverableIntent);
        }
    }
    
    private void connectDevice(Intent data, boolean secure) {
        // Get the device MAC address
        String address = data.getExtras()
            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
        // Get the BluetoothDevice object
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        // Attempt to connect to the device
        mChatService.connect(device, secure);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.option_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent serverIntent = null;
        switch (item.getItemId()) {
        case R.id.secure_connect_scan:
            // Launch the DeviceListActivity to see devices and do scan
            serverIntent = new Intent(this, DeviceListActivity.class);
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);
            return true;
        case R.id.insecure_connect_scan:
            // Launch the DeviceListActivity to see devices and do scan
            serverIntent = new Intent(this, DeviceListActivity.class);
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);
            return true;
        case R.id.discoverable:
            // Ensure this device is discoverable by others
            ensureDiscoverable();
            return true;
        }
        return false;
    }
    
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(D) Log.d(TAG, "onActivityResult " + resultCode);
        switch (requestCode) {
        case REQUEST_CONNECT_DEVICE_SECURE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
                connectDevice(data, true);
            }
            break;
        case REQUEST_CONNECT_DEVICE_INSECURE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
                connectDevice(data, false);
            }
            break;
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // Bluetooth is now enabled, so set up a chat session
            	setupBluetoothService();
            } else {
                // User did not enable Bluetooth or an error occurred
                Log.d(TAG, "BT not enabled");
                Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }
    
    private void setupBluetoothService() {
        if (mChatService == null) {
            // Initialize the BluetoothChatService to perform bluetooth connections
            mChatService = new BluetoothChatService(this, mHandler);
        }   	
    }
    
    /**
     * Sends a command message.
     * @param message  A string of text to send.
     */
    private void sendCmdMsgToPairedDevice(String message) {
        // Check that we're actually connected before trying anything
        if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
            return;
        }

        // Check that there's actually something to send
        if (message.length() > 0) {
            // Get the message bytes and tell the BluetoothChatService to write
            byte[] send = message.getBytes();
            mChatService.write(send);
        }
    }

	@Override
	public void goForwardLeft() {
		Toast.makeText(this, "forwardLeft", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_LEFT);
		sendCmdMsgToPairedDevice(CMD_LEFT);
		sendCommand(CMD_FORWARD);
		sendCmdMsgToPairedDevice(CMD_FORWARD);
	}

	@Override
	public void goForward() {
		Toast.makeText(this, "forward", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_ALL_STOP);
		sendCmdMsgToPairedDevice(CMD_ALL_STOP);
		sendCommand(CMD_FORWARD);
		sendCmdMsgToPairedDevice(CMD_FORWARD);
	}

	@Override
	public void goForwardRight() {
		Toast.makeText(this, "forwardRight", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_RIGHT);
		sendCmdMsgToPairedDevice(CMD_RIGHT);
		sendCommand(CMD_FORWARD);
		sendCmdMsgToPairedDevice(CMD_FORWARD);
	}

	@Override
	public void goBackwardLeft() {
		Toast.makeText(this, "backwardLeft", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_LEFT);
		sendCmdMsgToPairedDevice(CMD_LEFT);
		sendCommand(CMD_BACKWARD);
		sendCmdMsgToPairedDevice(CMD_BACKWARD);
	}

	@Override
	public void goBackward() {
		Toast.makeText(this, "backward", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_ALL_STOP);
		sendCmdMsgToPairedDevice(CMD_ALL_STOP);
		sendCommand(CMD_BACKWARD);
		sendCmdMsgToPairedDevice(CMD_BACKWARD);
	}

	@Override
	public void goBackwardRight() {
		Toast.makeText(this, "backwardRight", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_RIGHT);
		sendCmdMsgToPairedDevice(CMD_RIGHT);
		sendCommand(CMD_BACKWARD);
		sendCmdMsgToPairedDevice(CMD_BACKWARD);
	}

	@Override
	public void goStop() {
		Toast.makeText(this, "stop", Toast.LENGTH_SHORT).show();
		sendCommand(CMD_ALL_STOP);
		sendCmdMsgToPairedDevice(CMD_ALL_STOP);
	}
}